V38
authorjoan <joan@abyz.co.uk>
Fri, 2 Oct 2015 07:23:02 +0000 (08:23 +0100)
committerjoan <joan@abyz.co.uk>
Fri, 2 Oct 2015 07:23:02 +0000 (08:23 +0100)
17 files changed:
Makefile
command.c
pig2vcd.1
pigpio.3
pigpio.c
pigpio.h
pigpio.py
pigpiod.1
pigpiod.c
pigpiod_if.3
pigpiod_if.c
pigpiod_if.h
pigs.1
setup.py
x_pigpio.py
x_pigpiod_if.c
x_pigs

index 5bbe2af64903f2c9c5b10ec341bcacf1a9ff382e..bf8155e83a5fb95f0fb535f0ff7dc0298f024097 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -63,8 +63,8 @@ install:      $(ALL)
        install -m 0755 -s pig2vcd       /usr/local/bin
        install -m 0755 -s pigpiod       /usr/local/bin
        install -m 0755 -s pigs          /usr/local/bin
-       python2 setup.py install
-       python3 setup.py install
+       if which python2; then python2 setup.py install; fi
+       if which python3; then python3 setup.py install; fi
        install -m 0755 -d               /usr/local/man/man1
        install -m 0644 *.1              /usr/local/man/man1
        install -m 0755 -d               /usr/local/man/man3
@@ -79,12 +79,8 @@ uninstall:
        rm -f /usr/local/bin/pig2vcd
        rm -f /usr/local/bin/pigpiod
        rm -f /usr/local/bin/pigs
-       echo removing python2 files
-       python2 setup.py install --record /tmp/pigpio >/dev/null
-       xargs rm -f < /tmp/pigpio >/dev/null
-       echo removing python3 files
-       python3 setup.py install --record /tmp/pigpio >/dev/null
-       xargs rm -f < /tmp/pigpio >/dev/null
+       if which python2; then python2 setup.py install --record /tmp/pigpio >/dev/null; xargs rm -f < /tmp/pigpio >/dev/null; fi
+       if which python3; then python3 setup.py install --record /tmp/pigpio >/dev/null; xargs rm -f < /tmp/pigpio >/dev/null; fi
        rm -f /usr/local/man/man1/pig*.1
        rm -f /usr/local/man/man3/pig*.3
        ldconfig
index 1981fa5679a9d72bcacb019697b959a54b66bd35..4a8154372371a793394c7175ee84d7fe6d6bb941 100644 (file)
--- a/command.c
+++ b/command.c
@@ -26,7 +26,7 @@ For more information, please refer to <http://unlicense.org/>
 */
 
 /*
-This version is for pigpio version 37+
+This version is for pigpio version 38+
 */
 
 #include <stdio.h>
@@ -58,6 +58,9 @@ cmdInfo_t cmdInfo[]=
    {PI_CMD_CF1,   "CF1",   195, 2}, // gpioCustom1
    {PI_CMD_CF2,   "CF2",   195, 6}, // gpioCustom2
 
+   {PI_CMD_CGI,   "CGI",   101, 4}, // gpioCfgGetInternals
+   {PI_CMD_CSI,   "CSI",   111, 1}, // gpioCfgSetInternals
+
    {PI_CMD_GDC,   "GDC",   112, 2}, // gpioGetPWMdutycycle
    {PI_CMD_GPW,   "GPW",   112, 2}, // gpioGetServoPulsewidth
 
@@ -241,6 +244,9 @@ BS2 bits         Set gpios in bank 2\n\
 CF1 ...          Custom function 1\n\
 CF2 ...          Custom function 2\n\
 \n\
+CGI              Configuration get internals\n\
+CSI v            Configuration set internals\n\
+\n\
 GDC g            Get PWM dutycycle for gpio\n\
 GPW g            Get servo pulsewidth for gpio\n\
 \n\
@@ -450,6 +456,7 @@ static errInfo_t errInfo[]=
    {PI_BAD_HCLK_PASS    , "need password to use hardware clock 1"},
    {PI_HPWM_ILLEGAL     , "illegal, PWM in use for main clock"},
    {PI_BAD_DATABITS     , "serial data bits not 1-32"},
+   {PI_BAD_STOPBITS     , "serial (half) stop bits not 2-8"},
    {PI_MSG_TOOBIG       , "socket/pipe message too big"},
    {PI_BAD_MALLOC_MODE  , "bad memory allocation mode"},
    {PI_TOO_MANY_SEGS    , "too many I2C transaction segments"},
@@ -469,6 +476,9 @@ static errInfo_t errInfo[]=
    {PI_CHAIN_TOO_BIG    , "chain is too long"},
    {PI_DEPRECATED       , "deprecated function removed"},
    {PI_BAD_SER_INVERT   , "bit bang serial invert not 0 or 1"},
+   {PI_BAD_EDGE         , "bad ISR edge, not 1, 1, or 2"},
+   {PI_BAD_ISR_INIT     , "bad ISR initialisation"},
+   {PI_BAD_FOREVER      , "loop forever must be last chain command"},
 
 };
 
@@ -567,7 +577,7 @@ int cmdParse(
 
    switch (cmdInfo[idx].vt)
    {
-      case 101: /* BR1  BR2  H  HELP  HWVER
+      case 101: /* BR1  BR2  CGI  H  HELP  HWVER
                    DCRA  HALT  INRA  NO
                    PIGPV  POPA  PUSHA  RET  T  TICK  WVBSY  WVCLR
                    WVCRE  WVGO  WVGOR  WVHLT  WVNEW
@@ -578,8 +588,8 @@ int cmdParse(
 
          break;
 
-      case 111: /* BC1  BC2  BS1  BS2  
-                   ADD  AND  CMP  DIV  LDA  LDAB  MLT
+      case 111: /* ADD  AND  BC1  BC2  BS1  BS2  
+                   CMP  CSI  DIV  LDA  LDAB  MLT
                    MOD  OR  RLA  RRA  STAB  SUB  WAIT  XOR
 
                    One parameter, any value.
index 52c5a935f1644b84a1a814f3c6920692fb6bfdb7..65fab373e53371711d0556cf13a9019c08e61627 100644 (file)
--- a/pig2vcd.1
+++ b/pig2vcd.1
@@ -53,29 +53,22 @@ typedef struct
 .br
 
 .br
-seqno starts at 0 each time the handle is opened and then increments
-by one for each report.
+seqno: starts at 0 each time the handle is opened and then increments by one for each report.
 
 .br
 
 .br
-flags, if bit 5 is set then bits 0-4 of the flags indicate a gpio which
-has had a watchdog timeout.
+flags: two flags are defined, PI_NTFY_FLAGS_WDOG and PI_NTFY_FLAGS_ALIVE. If bit 5 is set (PI_NTFY_FLAGS_WDOG) then bits 0-4 of the flags indicate a gpio which has had a watchdog timeout; if bit 6 is set (PI_NTFY_FLAGS_ALIVE) this indicates a keep alive signal on the pipe/socket and is sent once a minute in the absence of other notification activity.
 
 .br
 
 .br
-tick is the number of microseconds since system boot.
+tick: the number of microseconds since system boot. It wraps around after 1h12m.
 
 .br
 
 .br
-level indicates the level of each gpio.
-
-.br
-
-.br
-pig2vcd takes these notifications and outputs a text format VCD.
+level: indicates the level of each gpio. If bit 1<<x is set then gpio x is high. pig2vcd takes these notifications and outputs a text format VCD.
 
 .br
 
index ad6888793611fa0a6af187024dab439c4cbe8162..1cb5d7164755196b78c9ea82ab6e3e365e67a5e1 100644 (file)
--- a/pigpio.3
+++ b/pigpio.3
@@ -1104,7 +1104,7 @@ void aFunction(int gpio, int level, uint32_t tick)
 .br
 
 .br
-gpioSetAlertFunc(4, aFunction);
+gpioSetAlertFunc(4F, aFunction);
 .br
 
 .EE
@@ -1155,6 +1155,135 @@ registered per gpio.
 .br
 See \fBgpioSetAlertFunc\fP for further details.
 
+.IP "\fBint gpioSetISRFunc(unsigned user_gpio, unsigned edge, int timeout, gpioISRFunc_t f)\fP"
+.IP "" 4
+Registers a function to be called (a callback) whenever the specified
+gpio interrupt occurs.
+
+.br
+
+.br
+
+.EX
+user_gpio: 0-31
+.br
+     edge: RISING_EDGE, FALLING_EDGE, or EITHER_EDGE
+.br
+  timeout: interrupt timeout in milliseconds (<=0 to cancel)
+.br
+        f: the callback function
+.br
+
+.EE
+
+.br
+
+.br
+Returns 0 if OK, otherwise PI_BAD_USER_GPIO, PI_BAD_EDGE,
+or PI_BAD_ISR_INIT.
+
+.br
+
+.br
+One function may be registered per gpio.
+
+.br
+
+.br
+The function is passed the gpio, the current level, and the
+current tick.  The level will be PI_TIMEOUT if the optional
+interrupt timeout expires.
+
+.br
+
+.br
+The underlying Linux sysfs gpio interface is used to provide
+the interrupt services.
+
+.br
+
+.br
+The first time the function is called, with a non-NULL f, the
+gpio is exported, set to be an input, and set to interrupt
+on the given edge and timeout.
+
+.br
+
+.br
+Subsequent calls, with a non-NULL f, can vary one or more of the
+edge, timeout, or function.
+
+.br
+
+.br
+The ISR may be cancelled by passing a NULL f, in which case the
+gpio is unexported.
+
+.br
+
+.br
+The tick is that read at the time the process was informed of
+the interrupt.  This will be a variable number of microseconds
+after the interrupt occurred.  Typically the latency will be of
+the order of 50 microseconds.  The latency is not guaranteed
+and will vary with system load.
+
+.br
+
+.br
+The level is that read at the time the process was informed of
+the interrupt, or PI_TIMEOUT if the optional interrupt timeout
+expired.  It may not be the same as the expected edge as
+interrupts happening in rapid succession may be missed by the
+kernel (i.e. this mechanism can not be used to capture several
+interrupts only a few microseconds apart).
+
+.IP "\fBint gpioSetISRFuncEx(unsigned user_gpio, unsigned edge, int timeout, gpioISRFuncEx_t f, void *userdata)\fP"
+.IP "" 4
+Registers a function to be called (a callback) whenever the specified
+gpio interrupt occurs.
+
+.br
+
+.br
+
+.EX
+user_gpio: 0-31
+.br
+     edge: RISING_EDGE, FALLING_EDGE, or EITHER_EDGE
+.br
+  timeout: interrupt timeout in milliseconds (<=0 to cancel)
+.br
+        f: the callback function
+.br
+ userdata: pointer to arbitrary user data
+.br
+
+.EE
+
+.br
+
+.br
+Returns 0 if OK, otherwise PI_BAD_USER_GPIO, PI_BAD_EDGE,
+or PI_BAD_ISR_INIT.
+
+.br
+
+.br
+The function is passed the gpio, the current level, the
+current tick, and the userdata pointer.
+
+.br
+
+.br
+Only one of \fBgpioSetISRFunc\fP or \fBgpioSetISRFuncEx\fP can be
+registered per gpio.
+
+.br
+
+.br
+See \fBgpioSetISRFunc\fP for further details.
+
 .IP "\fBint gpioNotifyOpen(void)\fP"
 .IP "" 4
 This function requests a free notification handle.
@@ -1905,18 +2034,26 @@ The following command codes are supported:
 .br
 
 .br
-Name          Cmd & Data   Meaning
+Name           Cmd & Data   Meaning
 
 .br
-Loop Start    255 0        Identify start of a wave block
+Loop Start     255 0        Identify start of a wave block
 
 .br
-Loop Repeat   255 1 x y    loop x + y*256 times
+Loop Repeat    255 1 x y    loop x + y*256 times
 
 .br
-Delay         255 2 x y    delay x + y*256 microseconds
+Delay          255 2 x y    delay x + y*256 microseconds
 
 .br
+Loop Forever   255 3        loop forever
+
+.br
+
+.br
+
+.br
+If present Loop Forever must be the last entry in the chain.
 
 .br
 
@@ -5064,7 +5201,7 @@ method uses the mailbox property interface to allocate bus memory.
 Auto will use the mailbox method unless a larger than default buffer
 size is requested with \fBgpioCfgBufferSize\fP.
 
-.IP "\fBint gpioCfgInternals(unsigned cfgWhat, int cfgVal)\fP"
+.IP "\fBint gpioCfgInternals(unsigned cfgWhat, unsigned cfgVal)\fP"
 .IP "" 4
 Used to tune internal settings.
 
@@ -5080,6 +5217,26 @@ cfgWhat: see source code
 
 .EE
 
+.IP "\fBuint32_t gpioCfgGetInternals(void)\fP"
+.IP "" 4
+This function returns the current library internal configuration
+settings.
+
+.IP "\fBint gpioCfgSetInternals(uint32_t cfgVal)\fP"
+.IP "" 4
+This function sets the current library internal configuration
+settings.
+
+.br
+
+.br
+
+.EX
+cfgVal: see source code
+.br
+
+.EE
+
 .IP "\fBint gpioCustom1(unsigned arg1, unsigned arg2, char *argx, unsigned argc)\fP"
 .IP "" 4
 This function is available for user customisation.
@@ -5698,9 +5855,9 @@ The hardware clock frequency.
 .br
 
 .EX
-#define PI_HW_CLK_MIN_FREQ 4689
+PI_HW_CLK_MIN_FREQ 4689
 .br
-#define PI_HW_CLK_MAX_FREQ 250000000
+PI_HW_CLK_MAX_FREQ 250000000
 .br
 
 .EE
@@ -5734,9 +5891,9 @@ waveform.
 .br
 
 .EX
-#define PI_MIN_WAVE_DATABITS 1
+PI_MIN_WAVE_DATABITS 1
 .br
-#define PI_MAX_WAVE_DATABITS 32
+PI_MAX_WAVE_DATABITS 32
 .br
 
 .EE
@@ -5787,6 +5944,28 @@ The number may vary between 0 and range (default 255) where
 
 .br
 
+.IP "\fBedge\fP: 0-2" 0
+The type of gpio edge to generate an intrrupt.  See\fBgpioSetISRFunc\fP,
+and \fBgpioSetISRFuncEx\fP.
+
+.br
+
+.br
+
+.EX
+RISING_EDGE 0
+.br
+FALLING_EDGE 1
+.br
+EITHER_EDGE 2
+.br
+
+.EE
+
+.br
+
+.br
+
 .IP "\fBf\fP" 0
 
 .br
@@ -5964,6 +6143,34 @@ typedef void (*gpioGetSamplesFuncEx_t)
 
 .br
 
+.IP "\fBgpioISRFunc_t\fP" 0
+
+.EX
+typedef void (*gpioISRFunc_t)
+.br
+   (int gpio, int level, uint32_t tick);
+.br
+
+.EE
+
+.br
+
+.br
+
+.IP "\fBgpioISRFuncEx_t\fP" 0
+
+.EX
+typedef void (*gpioISRFuncEx_t)
+.br
+   (int gpio, int level, uint32_t tick, void *userdata);
+.br
+
+.EE
+
+.br
+
+.br
+
 .IP "\fBgpioPulse_t\fP" 0
 
 .EX
@@ -6551,7 +6758,7 @@ The hardware PWM dutycycle.
 .br
 
 .EX
-#define PI_HW_PWM_RANGE 1000000
+PI_HW_PWM_RANGE 1000000
 .br
 
 .EE
@@ -6568,9 +6775,9 @@ The hardware PWM frequency.
 .br
 
 .EX
-#define PI_HW_PWM_MIN_FREQ 1
+PI_HW_PWM_MIN_FREQ 1
 .br
-#define PI_HW_PWM_MAX_FREQ 125000000
+PI_HW_PWM_MAX_FREQ 125000000
 .br
 
 .EE
@@ -6940,9 +7147,9 @@ to a waveform.
 .br
 
 .EX
-#define PI_MIN_WAVE_HALFSTOPBITS 2
+PI_MIN_WAVE_HALFSTOPBITS 2
 .br
-#define PI_MAX_WAVE_HALFSTOPBITS 8
+PI_MAX_WAVE_HALFSTOPBITS 8
 .br
 
 .EE
@@ -6959,7 +7166,12 @@ An array of characters.
 .br
 
 .IP "\fBtimeout\fP" 0
-A gpio watchdog timeout in milliseconds.
+A gpio level change timeout in milliseconds.
+
+.br
+
+.br
+\fBgpioSetWatchdog\fP
 
 .EX
 PI_MIN_WDOG_TIMEOUT 0
@@ -6971,6 +7183,19 @@ PI_MAX_WDOG_TIMEOUT 60000
 
 .br
 
+.br
+\fBgpioSetISRFunc\fP and \fBgpioSetISRFuncEx\fP
+
+.EX
+<=0 cancel timeout
+.br
+>0 timeout after specified milliseconds
+.br
+
+.EE
+
+.br
+
 .br
 
 .IP "\fBtimer\fP" 0
@@ -7360,6 +7585,12 @@ A 16-bit word value.
 #define PI_CMD_SLRI  94
 .br
 
+.br
+#define PI_CMD_CGI   95
+.br
+#define PI_CMD_CSI   96
+.br
+
 .br
 #define PI_CMD_NOIB  99
 .br
@@ -7618,6 +7849,12 @@ A 16-bit word value.
 .br
 #define PI_BAD_SER_INVERT  -121 // bit bang serial invert not 0 or 1
 .br
+#define PI_BAD_EDGE        -122 // bad ISR edge value, not 0-2
+.br
+#define PI_BAD_ISR_INIT    -123 // bad ISR initialisation
+.br
+#define PI_BAD_FOREVER     -124 // loop forever must be last chain command
+.br
 
 .br
 #define PI_PIGIF_ERR_0    -2000
@@ -7672,6 +7909,10 @@ A 16-bit word value.
 #define PI_DEFAULT_MEM_ALLOC_MODE        PI_MEM_ALLOC_AUTO
 .br
 
+.br
+#define PI_DEFAULT_CFG_INTERNALS         0
+.br
+
 .br
 
 .EE
index f330ef030b2aa934451786ca220c14f01fdf3772..191226a41ba22c22dadf6777436ab314eed57a00 100644 (file)
--- a/pigpio.c
+++ b/pigpio.c
@@ -25,7 +25,7 @@ OTHER DEALINGS IN THE SOFTWARE.
 For more information, please refer to <http://unlicense.org/>
 */
 
-/* pigpio version 37 */
+/* pigpio version 38 */
 
 /* include ------------------------------------------------------- */
 
@@ -867,36 +867,48 @@ typedef struct
 {
    callbk_t func;
    unsigned ex;
-   void *   userdata;
-   int      timeout;
+   void *userdata;
+   int timeout;
    uint32_t tick;
 } gpioAlert_t;
 
+typedef struct
+{
+   unsigned gpio;
+   pthread_t *pth;
+   callbk_t func;
+   unsigned edge;
+   int timeout;
+   unsigned ex;
+   void *userdata;
+   int inited;
+} gpioISR_t;
+
 typedef struct
 {
    callbk_t func;
    unsigned ex;
-   void *   userdata;
+   void *userdata;
 } gpioSignal_t;
 
 typedef struct
 {
    callbk_t func;
    unsigned ex;
-   void *   userdata;
+   void *userdata;
    uint32_t bits;
 } gpioGetSamples_t;
 
 typedef struct
 {
-   callbk_t        func;
-   unsigned        ex;
-   void *          userdata;
-   unsigned        id;
-   unsigned        running;
-   unsigned        millis;
+   callbk_t func;
+   unsigned ex;
+   void *userdata;
+   unsigned id;
+   unsigned running;
+   unsigned millis;
    struct timespec nextTick;
-   pthread_t       pthId;
+   pthread_t pthId;
 } gpioTimer_t;
 
 typedef struct
@@ -928,6 +940,7 @@ typedef struct
    uint32_t lastReportTick;
    int      fd;
    int      pipe;
+   int      max_emits;
 } gpioNotify_t;
 
 typedef struct
@@ -955,8 +968,9 @@ typedef struct
 
 typedef struct
 {
-   uint32_t startTick;
    uint32_t alertTicks;
+   uint32_t lateTicks;
+   uint32_t moreToDo;
    uint32_t diffTick[TICKSLOTS];
    uint32_t cbTicks;
    uint32_t cbCalls;
@@ -966,6 +980,9 @@ typedef struct
    uint32_t numSamples;
    uint32_t DMARestarts;
    uint32_t dmaInitCbsCount;
+   uint32_t goodPipeWrite;
+   uint32_t shortPipeWrite;
+   uint32_t wouldBlockPipeWrite;
 } gpioStats_t;
 
 typedef struct
@@ -977,9 +994,14 @@ typedef struct
    unsigned DMAsecondaryChannel;
    unsigned socketPort;
    unsigned ifFlags;
-   int      dbgLevel;
-   unsigned showStats;
    unsigned memAllocMode;
+   unsigned dbgLevel;
+   unsigned alertFreq;
+   uint32_t internals;
+      /*
+      0-3: dbgLevel
+      4-7: alertFreq
+      */
 } gpioCfg_t;
 
 typedef struct
@@ -1131,6 +1153,8 @@ static int pthSocketRunning = 0;
 
 static gpioAlert_t      gpioAlert  [PI_MAX_USER_GPIO+1];
 
+static gpioISR_t        gpioISR    [PI_MAX_USER_GPIO+1];
+
 static gpioGetSamples_t gpioGetSamples;
 
 static gpioInfo_t       gpioInfo   [PI_MAX_GPIO+1];
@@ -1199,9 +1223,10 @@ static volatile gpioCfg_t gpioCfg =
    PI_DEFAULT_DMA_SECONDARY_CHANNEL,
    PI_DEFAULT_SOCKET_PORT,
    PI_DEFAULT_IF_FLAGS,
-   DBG_MIN_LEVEL,
-   0,
    PI_DEFAULT_MEM_ALLOC_MODE,
+   0, /* dbgLevel */
+   0, /* alertFreq */
+   0, /* internals */
 };
 
 /* no initialisation required */
@@ -1413,6 +1438,14 @@ static void myGpioSetMode(unsigned gpio, unsigned mode)
 }
 
 
+/* ----------------------------------------------------------------------- */
+
+static int myGpioRead(unsigned gpio)
+{
+   if ((*(gpioReg + GPLEV0 + BANK) & BIT) != 0) return PI_ON;
+   else                                         return PI_OFF;
+}
+
 
 /* ----------------------------------------------------------------------- */
 
@@ -1552,11 +1585,12 @@ static uint32_t myGetTick(int pos)
 
 static int myPermit(unsigned gpio)
 {
-   if ((gpio <= PI_MAX_GPIO) &&
-       (gpioMask & ((uint64_t)(1)<<gpio)))
-      return 1;
-   else
-      return 0;
+   if (gpio <= PI_MAX_GPIO)
+   {
+      if (gpioMask & ((uint64_t)(1)<<gpio)) return 1;
+      else return 0;
+   }
+   return 1; /* will fail for bad gpio number */
 }
 
 static void flushMemory(void)
@@ -1689,7 +1723,6 @@ static int myDoCommand(uint32_t *p, unsigned bufSize, char *buf)
          res = gpioCustom1(p[1], p[2], buf, p[3]);
          break;
 
-
       case PI_CMD_CF2:
          /* a couple of extra precautions for untrusted code */
          if (p[2] > bufSize) p[2] = bufSize;
@@ -1697,6 +1730,10 @@ static int myDoCommand(uint32_t *p, unsigned bufSize, char *buf)
          if (res > p[2]) res = p[2];
          break;
 
+      case PI_CMD_CGI: res = gpioCfgGetInternals(); break;
+
+      case PI_CMD_CSI: res = gpioCfgSetInternals(p[1]); break;
+
       case PI_CMD_GDC: res = gpioGetPWMdutycycle(p[1]); break;
 
       case PI_CMD_GPW: res = gpioGetServoPulsewidth(p[1]); break;
@@ -2226,14 +2263,11 @@ static void myGpioSetPwm(unsigned gpio, int oldVal, int newVal)
 
 static void myGpioSetServo(unsigned gpio, int oldVal, int newVal)
 {
-   int switchGpioOff;
    int newOff, oldOff, realRange, cycles, i;
 
    DBG(DBG_INTERNAL,
       "myGpioSetServo %d from %d to %d", gpio, oldVal, newVal);
 
-   switchGpioOff = 0;
-
    realRange = pwmRealRange[clkCfg[gpioCfg.clockMicros].servoIdx];
    cycles    = pwmCycles   [clkCfg[gpioCfg.clockMicros].servoIdx];
 
@@ -2267,16 +2301,14 @@ static void myGpioSetServo(unsigned gpio, int oldVal, int newVal)
          for (i=0; i<SUPERCYCLE; i+=cycles)
             myClearGpioOn(gpio, i);
 
-         for (i=0; i<SUPERLEVEL; i+=realRange)
-            myClearGpioOff(gpio, i+oldOff);
+         /* if in pulse then delay for the last cycle to complete */
 
-         switchGpioOff = 1;
-      }
+         if (myGpioRead(gpio)) myGpioDelay(PI_MAX_SERVO_PULSEWIDTH);
 
-      if (switchGpioOff)
-      {
-         *(gpioReg + GPCLR0) = (1<<gpio);
-         *(gpioReg + GPCLR0) = (1<<gpio);
+         /* deschedule gpio off */
+
+         for (i=0; i<SUPERLEVEL; i+=realRange)
+            myClearGpioOff(gpio, i+oldOff);
       }
    }
 }
@@ -4920,11 +4952,14 @@ static void sigHandler(int signum)
                break;
 
             case SIGPIPE:
-            case SIGCHLD:
             case SIGWINCH:
                DBG(DBG_USER, "signal %d ignored", signum);
                break;
 
+            case SIGCHLD:
+               /* Used to notify threads of events */
+               break;
+
             default:
                DBG(DBG_ALWAYS, "Unhandled signal %d, terminating\n", signum);
                gpioTerminate();
@@ -4959,6 +4994,12 @@ static void sigSetHandler(void)
    }
 }
 
+unsigned alert_delays[]={
+   1000, 1068, 1145, 1235,
+   1339, 1463, 1613, 1796,
+   2027, 2326, 2727, 3297,
+   4167, 5660, 8823, 20000};
+
 /* ======================================================================= */
 
 static void * pthAlertThread(void *x)
@@ -4966,7 +5007,7 @@ static void * pthAlertThread(void *x)
    struct timespec req, rem;
    uint32_t oldLevel, newLevel, level, reportedLevel;
    uint32_t oldSlot,  newSlot;
-   uint32_t tick, expected;
+   uint32_t tick, expected, nowTick;
    int32_t diff;
    int cycle, pulse;
    int emit, seqno, emitted;
@@ -4975,6 +5016,10 @@ static void * pthAlertThread(void *x)
    int b, n, v;
    int err;
    int stopped;
+   int delayTicks;
+   uint32_t nextWakeTick;
+   int moreToDo;
+   int max_emits;
    char fifo[32];
 
    req.tv_sec = 0;
@@ -4985,10 +5030,6 @@ static void * pthAlertThread(void *x)
 
    reportedLevel = gpioReg[GPLEV0];
 
-   tick = systReg[SYST_CLO];
-
-   gpioStats.startTick = tick;
-
    oldSlot = dmaCurrentSlot(dmaNowAtICB());
 
    cycle = (oldSlot/PULSE_PER_CYCLE);
@@ -4996,48 +5037,15 @@ static void * pthAlertThread(void *x)
 
    stopped = 0;
 
-   while (1)
-   {
-
-      if (dmaIn[DMA_CONBLK_AD])
-      {
-         if (stopped)
-         {
-            DBG(DBG_STARTUP, "****** GOING ******");
-            stopped = 0;
-         }
-      }
-      else
-      {
-         stopped = 1;
-
-         myGpioDelay(5000);
-
-         if (runState == PI_RUNNING)
-         {
-            /* should never be executed, leave code just in case */
-
-            gpioCfg.showStats = 1;
-
-            dmaInitCbs();
-            flushMemory();
-            initDMAgo((uint32_t *)dmaIn, (uint32_t)dmaIBus[0]);
-            myGpioDelay(5000); /* let DMA run for a while */
-            oldSlot = dmaCurrentSlot(dmaNowAtICB());
-            gpioStats.DMARestarts++;
-         }
-      }
-
-      gpioStats.alertTicks++;
+   moreToDo = 0;
 
-      req.tv_nsec = 850000;
+   tick = systReg[SYST_CLO];
 
-      while (nanosleep(&req, &rem))
-      {
-         req.tv_sec  = rem.tv_sec;
-         req.tv_nsec = rem.tv_nsec;
-      }
+   nextWakeTick =
+      tick + alert_delays[(gpioCfg.internals>>PI_CFG_ALERT_FREQ)&15];
 
+   while (1)
+   {
       newSlot = dmaCurrentSlot(dmaNowAtICB());
 
       numSamples = 0;
@@ -5086,19 +5094,11 @@ static void * pthAlertThread(void *x)
 
             if (diff < 0)
             {
-               /* shouldn't happen */
-
-               //gpioCfg.showStats = 1;
-
                gpioStats.diffTick[0]++;
             }
 
             else if (diff >= TICKSLOTS)
             {
-               /* shouldn't happen */
-
-               //gpioCfg.showStats = 1;
-
                gpioStats.diffTick[TICKSLOTS-1]++;
             }
 
@@ -5106,6 +5106,8 @@ static void * pthAlertThread(void *x)
          }
       }
 
+      if (oldSlot == newSlot) moreToDo = 0; else moreToDo = 1;
+
       /* should gpioGetSamples be called */
 
       if (changedBits)
@@ -5318,6 +5320,7 @@ static void * pthAlertThread(void *x)
             if (emit)
             {
                gpioNotify[n].lastReportTick = tick;
+               max_emits = gpioNotify[n].max_emits;
 
                if (emit > gpioStats.maxEmit) gpioStats.maxEmit = emit;
 
@@ -5325,15 +5328,15 @@ static void * pthAlertThread(void *x)
 
                while (emit > 0)
                {
-                  if (emit > MAX_EMITS)
+                  if (emit > max_emits)
                   {
                      gpioStats.emitFrags++;
 
                      err = write(gpioNotify[n].fd,
                               gpioReport+emitted,
-                              MAX_EMITS*sizeof(gpioReport_t));
+                              max_emits*sizeof(gpioReport_t));
 
-                     if (err != (MAX_EMITS*sizeof(gpioReport_t)))
+                     if (err != (max_emits*sizeof(gpioReport_t)))
                      {
                         if (err < 0)
                         {
@@ -5351,11 +5354,20 @@ static void * pthAlertThread(void *x)
                               intNotifyBits();
                               break;
                            }
+                           else gpioStats.wouldBlockPipeWrite++;
+                        }
+                        else
+                        {
+                           gpioCfg.internals |= PI_CFG_STATS;
+                           gpioStats.shortPipeWrite++;
+                           DBG(DBG_ALWAYS, "emitted %d, asked for %d",
+                              err/sizeof(gpioReport_t), max_emits);
                         }
                      }
+                     else gpioStats.goodPipeWrite++;
 
-                     emitted += MAX_EMITS;
-                     emit    -= MAX_EMITS;
+                     emitted += max_emits;
+                     emit    -= max_emits;
                   }
                   else
                   {
@@ -5380,8 +5392,17 @@ static void * pthAlertThread(void *x)
                               intNotifyBits();
                               break;
                            }
+                           else gpioStats.wouldBlockPipeWrite++;
+                        }
+                        else
+                        {
+                           gpioCfg.internals |= PI_CFG_STATS;
+                           gpioStats.shortPipeWrite++;
+                           DBG(DBG_ALWAYS, "emitted %d, asked for %d",
+                              err/sizeof(gpioReport_t), emit);
                         }
                      }
+                     else gpioStats.goodPipeWrite++;
 
                      emitted += emit;
                      emit = 0;
@@ -5423,6 +5444,91 @@ static void * pthAlertThread(void *x)
          gpioStats.maxSamples = numSamples;
 
       gpioStats.numSamples += numSamples;
+
+      /* Check that DMA is running okay */
+
+      if (dmaIn[DMA_CONBLK_AD])
+      {
+         if (stopped)
+         {
+            DBG(DBG_STARTUP, "****** GOING ******");
+            stopped = 0;
+         }
+      }
+      else
+      {
+         stopped = 1;
+
+         myGpioDelay(5000);
+
+         if (runState == PI_RUNNING)
+         {
+            /* should never be executed, leave code just in case */
+
+            gpioCfg.internals |= PI_CFG_STATS;
+
+            dmaInitCbs();
+            flushMemory();
+            initDMAgo((uint32_t *)dmaIn, (uint32_t)dmaIBus[0]);
+            myGpioDelay(5000); /* let DMA run for a while */
+            oldSlot = dmaCurrentSlot(dmaNowAtICB());
+            gpioStats.DMARestarts++;
+         }
+      }
+
+      nowTick = systReg[SYST_CLO];
+
+      if (moreToDo)
+      {
+         gpioStats.moreToDo++;
+
+         /* rebase wake up time */
+
+         nextWakeTick = nowTick +
+            alert_delays[(gpioCfg.internals>>PI_CFG_ALERT_FREQ)&15];
+
+         req.tv_nsec = 0;
+      }
+      else
+      {
+         delayTicks = nextWakeTick - nowTick;
+
+         if (delayTicks < 0)
+         {
+            gpioStats.lateTicks++;
+
+            /* rebase wake up time */
+
+            nextWakeTick = nowTick +
+               alert_delays[(gpioCfg.internals>>PI_CFG_ALERT_FREQ)&15];
+
+            req.tv_nsec = 0;
+         }
+         else
+         {
+            gpioStats.alertTicks++;
+
+            nextWakeTick +=
+               alert_delays[(gpioCfg.internals>>PI_CFG_ALERT_FREQ)&15];
+
+            req.tv_nsec = (delayTicks * 1000);
+         }
+      }
+
+      if (req.tv_nsec)
+      {
+         req.tv_sec = 0;
+
+         while (nanosleep(&req, &rem))
+         {
+            req.tv_sec  = rem.tv_sec;
+            req.tv_nsec = rem.tv_nsec;
+         }
+      }
+
+      nextWakeTick +=
+         alert_delays[(gpioCfg.internals>>PI_CFG_ALERT_FREQ)&15];
+
    }
 
    return 0;
@@ -5960,12 +6066,14 @@ static void *pthSocketThreadHandler(void *fdC)
       switch (p[0])
       {
          case PI_CMD_NOIB:
+
             p[3] = gpioNotifyOpenInBand(sock);
 
-            /* Enable the Nagle algorithm. */
+           /* Enable the Nagle algorithm. */
             opt = 0;
             setsockopt(
                sock, IPPROTO_TCP, TCP_NODELAY, (char*)&opt, sizeof(int));
+
             break;
 
          case PI_CMD_PROCP:
@@ -7041,7 +7149,8 @@ int initInitialise(void)
 
    param.sched_priority = sched_get_priority_max(SCHED_FIFO);
 
-   sched_setscheduler(0, SCHED_FIFO, &param);
+   if (gpioCfg.internals & PI_CFG_RT_PRIORITY)
+      sched_setscheduler(0, SCHED_FIFO, &param);
 
    initClock(1); /* initialise main clock */
 
@@ -7350,12 +7459,17 @@ void gpioTerminate(void)
    if (dmaReg != MAP_FAILED) dmaOut[DMA_CS] = DMA_CHANNEL_RESET;
 
 #ifndef EMBEDDED_IN_VM
-   if (gpioCfg.showStats)
+   if (gpioCfg.internals & PI_CFG_STATS)
    {
+      fprintf(stderr,
+         "\n#####################################################\n");
       fprintf(stderr,
          "If you didn't request stats please cut & paste the\n"
          "following and e-mail to pigpio@abyz.co.uk\n");
 
+      fprintf(stderr, "pigpio version=%d internals=%X\n",
+         PIGPIO_VERSION, gpioCfg.internals);
+
       fprintf(stderr,
          "micros=%d allocMode=%d dmaInitCbs=%d DMARestarts=%d\n",
          gpioCfg.clockMicros, gpioCfg.memAllocMode,
@@ -7366,18 +7480,21 @@ void gpioTerminate(void)
          gpioStats.numSamples, gpioStats.maxSamples,
          gpioStats.maxEmit, gpioStats.emitFrags);
 
-      fprintf(stderr, "cbTicks %d, cbCalls %u alertTicks %u\n",
-         gpioStats.cbTicks, gpioStats.cbCalls, gpioStats.alertTicks);
+      fprintf(stderr, "cbTicks %d, cbCalls %u\n",
+         gpioStats.cbTicks, gpioStats.cbCalls);
 
-      for (i=0; i< TICKSLOTS; i++)
-         fprintf(stderr, "%9u ", gpioStats.diffTick[i]);
+      fprintf(stderr, "pipe: good %u, short %u, would block %u\n",
+         gpioStats.goodPipeWrite, gpioStats.shortPipeWrite,
+         gpioStats.wouldBlockPipeWrite);
 
-      fprintf(stderr, "\n");
+      fprintf(stderr, "alertTicks %u, lateTicks %u, moreToDo %u\n",
+         gpioStats.alertTicks, gpioStats.lateTicks, gpioStats.moreToDo);
 
-      fprintf(stderr, "\n");
+      for (i=0; i< TICKSLOTS; i++)
+         fprintf(stderr, "%9u ", gpioStats.diffTick[i]);
 
       fprintf(stderr,
-         "#####################################################\n");
+         "\n#####################################################\n\n\n");
    }
 #endif
 
@@ -8637,6 +8754,7 @@ int gpioWaveChain(char *buf, unsigned bufSize)
                   "chain counters nested too deep (at %d)", i);
 
             stk_pos[stk_lev++] = cb;
+
             i += 2;
          }
          else if (cmd == 1) /* loop end */
@@ -8708,7 +8826,6 @@ int gpioWaveChain(char *buf, unsigned bufSize)
 
                counters++;
             }
-            loop = -1;
          }
          else if (cmd == 2) /* delay us */
          {
@@ -8751,6 +8868,34 @@ int gpioWaveChain(char *buf, unsigned bufSize)
                p->next   = waveCbPOadr(chainGetCB(cb));
             }
          }
+         else if (cmd == 3) /* repeat loop forever */
+         {
+            i += 2;
+
+            loop = 0;
+            if (--stk_lev >= 0) loop = stk_pos[stk_lev];
+
+            if ((loop < 1) || (loop == cb))
+               SOFT_ERROR(PI_BAD_CHAIN_LOOP,
+                  "empty chain loop (at %d)", i);
+
+            chaincb = chainGetCB(cb++);
+            if (chaincb < 0)
+               SOFT_ERROR(PI_CHAIN_TOO_BIG, "chain is too long (%d)", cb);
+
+            if (i < bufSize)
+               SOFT_ERROR(PI_BAD_FOREVER,
+                  "loop forever must be last command");
+
+            p = rawWaveCBAdr(chaincb);
+
+            /* dummy src and dest */
+            p->info = NORMAL_DMA;
+            p->src = (uint32_t) (&dmaOBus[0]->periphData);
+            p->dst = (uint32_t) (&dmaOBus[0]->periphData);
+            p->length = 4;
+            p->next = waveCbPOadr(chainGetCB(loop));
+         }
          else
             SOFT_ERROR(PI_BAD_CHAIN_CMD,
                "unknown chain command (255 %d)", cmd);
@@ -9495,6 +9640,212 @@ int gpioSetAlertFuncEx(unsigned gpio, gpioAlertFuncEx_t f, void *userdata)
    return 0;
 }
 
+static void *pthISRThread(void *x)
+{
+   gpioISR_t *isr = x;
+   int fd;
+   int retval;
+   uint32_t tick;
+   int level;
+   uint32_t levels;
+   struct pollfd pfd;
+   char buf[64];
+
+   DBG(DBG_USER, "gpio=%d edge=%d timeout=%d f=%x u=%d data=%x",
+      isr->gpio, isr->edge, isr->timeout, (uint32_t)isr->func,
+      isr->ex, (uint32_t)isr->userdata);
+
+   sprintf(buf, "/sys/class/gpio/gpio%d/value", isr->gpio);
+
+   if ((fd = open(buf, O_RDONLY)) < 0)
+   {
+      DBG(DBG_ALWAYS, "gpio %d not exported", isr->gpio);
+      return NULL;
+   }
+
+   pfd.fd = fd;
+
+   pfd.events = POLLPRI;
+
+   lseek(fd, 0, SEEK_SET);    /* consume any prior interrupt */
+   read(fd, buf, sizeof buf);
+
+   while (1)
+   {
+      retval = poll(&pfd, 1, isr->timeout); /* wait for interrupt */
+
+      tick = systReg[SYST_CLO];
+
+      levels = *(gpioReg + GPLEV0);
+
+      if (retval >= 0)
+      {
+         lseek(fd, 0, SEEK_SET);    /* consume interrupt */
+         read(fd, buf, sizeof buf);
+         if (retval)
+         {
+            if (levels & (1<<isr->gpio)) level = PI_ON; else level = PI_OFF;
+         }
+         else level = PI_TIMEOUT;
+
+         if (isr->ex) (isr->func)(isr->gpio, level, tick, isr->userdata);
+         else         (isr->func)(isr->gpio, level, tick);
+      }
+   }
+
+   return NULL;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static int intGpioSetISRFunc(
+   unsigned gpio,
+   unsigned edge,
+   int timeout,
+   void *f,
+   int user,
+   void *userdata)
+{
+   char buf[64];
+
+   char *edge_str[]={"rising\n", "falling\n", "both\n"};
+   int fd;
+   int err;
+
+   DBG(DBG_INTERNAL,
+      "gpio=%d edge=%d timeout=%d function=%08X user=%d userdata=%08X",
+      gpio, edge, timeout, (uint32_t)f, user, (uint32_t)userdata);
+
+   if (f)
+   {
+      if (!gpioISR[gpio].inited) /* export gpio if unexported */
+      {
+         fd = open("/sys/class/gpio/export", O_WRONLY);
+         if (fd < 0) return PI_BAD_ISR_INIT;
+
+         /* ignore write fail if already exported */
+         sprintf(buf, "%d\n", gpio);
+         err = write(fd, buf, strlen(buf));
+         close(fd);
+
+         sprintf(buf, "/sys/class/gpio/gpio%d/direction", gpio);
+         fd = open(buf, O_WRONLY);
+         if (fd < 0) return PI_BAD_ISR_INIT;
+
+         err = write(fd, "in\n", 3);
+         close(fd);
+         if (err != 3) return PI_BAD_ISR_INIT;
+
+         gpioISR[gpio].gpio = gpio;
+         gpioISR[gpio].edge = -1;
+         gpioISR[gpio].timeout = -1;
+
+         gpioISR[gpio].inited = 1;
+      }
+
+      if (gpioISR[gpio].edge != edge)
+      {
+         sprintf(buf, "/sys/class/gpio/gpio%d/edge", gpio);
+         fd = open(buf, O_WRONLY);
+         if (fd < 0) return PI_BAD_ISR_INIT;
+
+         err = write(fd, edge_str[edge], strlen(edge_str[edge]));
+         close(fd);
+         if (err != strlen(edge_str[edge])) return PI_BAD_ISR_INIT;
+
+         gpioISR[gpio].edge = edge;
+
+         if (gpioISR[gpio].pth != NULL)
+            pthread_kill(*gpioISR[gpio].pth, SIGCHLD);
+      }
+
+      if (timeout <= 0) timeout = -1;
+      if (gpioISR[gpio].timeout != timeout)
+      {
+         gpioISR[gpio].timeout = timeout;
+
+         if (gpioISR[gpio].pth != NULL)
+            pthread_kill(*gpioISR[gpio].pth, SIGCHLD);
+      }
+
+      gpioISR[gpio].func = f;
+      gpioISR[gpio].ex = user;
+      gpioISR[gpio].userdata = userdata;
+
+      if (gpioISR[gpio].pth == NULL)
+         gpioISR[gpio].pth = gpioStartThread(pthISRThread, &gpioISR[gpio]);
+   }
+   else /* null function, delete ISR, unexport gpio */
+   {
+      if (gpioISR[gpio].pth) /* delete any existing ISR */
+      {
+         gpioStopThread(gpioISR[gpio].pth);
+         gpioISR[gpio].func = NULL;
+         gpioISR[gpio].pth = NULL;
+      }
+
+      if (gpioISR[gpio].inited) /* unexport any gpio */
+      {
+         fd = open("/sys/class/gpio/unexport", O_WRONLY);
+         if (fd < 0) return PI_BAD_ISR_INIT;
+         sprintf(buf, "%d\n", gpio);
+         err = write(fd, buf, strlen(buf));
+         close(fd);
+         if (err != sizeof(buf)) return PI_BAD_ISR_INIT;
+         gpioISR[gpio].inited = 0;
+      }
+   }
+
+   return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetISRFunc(
+   unsigned gpio,
+   unsigned edge,
+   int timeout,
+   gpioISRFunc_t f)
+{
+   DBG(DBG_USER, "gpio=%d edge=%d timeout=%d function=%08X",
+      gpio, edge, timeout, (uint32_t)f);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+   if (edge > EITHER_EDGE)
+      SOFT_ERROR(PI_BAD_EDGE, "bad ISR edge (%d)", edge);
+
+   return intGpioSetISRFunc(gpio, edge, timeout, f, 0, NULL);
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetISRFuncEx(
+   unsigned gpio,
+   unsigned edge,
+   int timeout,
+   gpioAlertFuncEx_t f,
+   void *userdata)
+{
+   DBG(DBG_USER, "gpio=%d edge=%d timeout=%d function=%08X userdata=%08X",
+      gpio, edge, timeout, (uint32_t)f, (uint32_t)userdata);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+   if (edge > EITHER_EDGE)
+      SOFT_ERROR(PI_BAD_EDGE, "bad ISR edge (%d)", edge);
+
+   return intGpioSetISRFunc(gpio, edge, timeout, f, 1, userdata);
+}
+
 
 /* ----------------------------------------------------------------------- */
 
@@ -9538,6 +9889,7 @@ int gpioNotifyOpen(void)
    gpioNotify[slot].bits  = 0;
    gpioNotify[slot].fd    = fd;
    gpioNotify[slot].pipe  = 1;
+   gpioNotify[slot].max_emits  = MAX_EMITS;
    gpioNotify[slot].lastReportTick = gpioTick();
 
    return slot;
@@ -9572,6 +9924,7 @@ static int gpioNotifyOpenInBand(int fd)
    gpioNotify[slot].bits  = 0;
    gpioNotify[slot].fd    = fd;
    gpioNotify[slot].pipe  = 0;
+   gpioNotify[slot].max_emits  = MAX_EMITS;
    gpioNotify[slot].lastReportTick = gpioTick();
 
    return slot;
@@ -9940,6 +10293,7 @@ void gpioStopThread(pthread_t *pth)
    {
       pthread_cancel(*pth);
       pthread_join(*pth, NULL);
+      free(pth);
    }
 }
 
@@ -10793,34 +11147,33 @@ int gpioCfgMemAlloc(unsigned memAllocMode)
 
 /* ----------------------------------------------------------------------- */
 
-int gpioCfgInternals(unsigned cfgWhat, int cfgVal)
+uint32_t gpioCfgGetInternals(void)
+{
+   return gpioCfg.internals;
+}
+
+int gpioCfgSetInternals(uint32_t cfgVal)
+{
+   gpioCfg.internals = cfgVal;
+   gpioCfg.dbgLevel = cfgVal & 0xF;
+   gpioCfg.alertFreq = (cfgVal>>4) & 0xF;
+   return 0;
+}
+
+int gpioCfgInternals(unsigned cfgWhat, unsigned cfgVal)
 {
    int retVal = PI_BAD_CFG_INTERNAL;
 
    DBG(DBG_USER, "cfgWhat=%u, cfgVal=%d", cfgWhat, cfgVal);
 
-   /*
-   133084774
-   207081315
-   293640712
-   394342930
-   472769257
-   430873902
-   635370313
-   684442696
-   786301093
-   816051706
-   858202631
-   997413601
-   */
-
    switch(cfgWhat)
    {
       case 562484977:
 
-         gpioCfg.showStats = cfgVal;
+         if (cfgVal) gpioCfg.internals |= PI_CFG_STATS;
+         else gpioCfg.internals &= (~PI_CFG_STATS);
 
-         DBG(DBG_ALWAYS, "showStats is %u", cfgVal);
+         DBG(DBG_ALWAYS, "show stats is %u", cfgVal);
 
          retVal = 0;
 
@@ -10828,15 +11181,16 @@ int gpioCfgInternals(unsigned cfgWhat, int cfgVal)
 
       case 984762879:
 
-         if (cfgVal < DBG_ALWAYS) cfgVal = DBG_ALWAYS;
-
-         if (cfgVal > DBG_MAX_LEVEL) cfgVal = DBG_MAX_LEVEL;
-
-         gpioCfg.dbgLevel = cfgVal;
+         if ((cfgVal >= DBG_ALWAYS) && (cfgVal <= DBG_MAX_LEVEL))
+         {
+            
+            gpioCfg.dbgLevel = cfgVal;
+            gpioCfg.internals = (gpioCfg.internals & (~0xF)) | cfgVal;
 
-         DBG(DBG_ALWAYS, "Debug level is %u", cfgVal);
+            DBG(DBG_ALWAYS, "Debug level is %u", cfgVal);
 
-         retVal = 0;
+            retVal = 0;
+         }
 
          break;
    }
@@ -10844,6 +11198,7 @@ int gpioCfgInternals(unsigned cfgWhat, int cfgVal)
    return retVal;
 }
 
+
 /* include any user customisations */
 
 #include "custom.cext"
index bbeb7100576553f322ab1b87bdb1954f57250fad..2d7700306b89ba8020ad3c63cdcb9adfe8828289 100644 (file)
--- a/pigpio.h
+++ b/pigpio.h
@@ -31,7 +31,7 @@ For more information, please refer to <http://unlicense.org/>
 #include <stdint.h>
 #include <pthread.h>
 
-#define PIGPIO_VERSION 37
+#define PIGPIO_VERSION 38
 
 /*TEXT
 
@@ -160,6 +160,9 @@ gpioGetPWMrealRange        Get underlying PWM range for a gpio
 
 gpioSetAlertFuncEx         Request a gpio change callback, extended
 
+gpioSetISRFunc             Request a gpio interrupt callback
+gpioSetISRFuncEx           Request a gpio interrupt callback, extended
+
 gpioSetSignalFunc          Request a signal callback
 gpioSetSignalFuncEx        Request a signal callback, extended
 
@@ -281,10 +284,14 @@ gpioCfgDMAchannel          Configure the DMA channel (DEPRECATED)
 gpioCfgDMAchannels         Configure the DMA channels
 gpioCfgPermissions         Configure the gpio access permissions
 gpioCfgInterfaces          Configure user interfaces
-gpioCfgInternals           Configure miscellaneous internals
 gpioCfgSocketPort          Configure socket port
 gpioCfgMemAlloc            Configure DMA memory allocation mode
 
+gpioCfgInternals           Configure miscellaneous internals (DEPRECATED)
+
+gpioCfgGetInternals        Get internal configuration settings
+gpioCfgSetInternals        Set internal configuration settings
+
 CUSTOM
 
 gpioCustom1                User custom function 1
@@ -448,6 +455,15 @@ typedef void (*gpioAlertFuncEx_t)  (int      gpio,
                                     uint32_t tick,
                                     void    *userdata);
 
+typedef void (*gpioISRFunc_t)      (int      gpio,
+                                    int      level,
+                                    uint32_t tick);
+
+typedef void (*gpioISRFuncEx_t)    (int      gpio,
+                                    int      level,
+                                    uint32_t tick,
+                                    void    *userdata);
+
 typedef void (*gpioTimerFunc_t)    (void);
 
 typedef void (*gpioTimerFuncEx_t)  (void *userdata);
@@ -720,6 +736,22 @@ typedef void *(gpioThreadFunc_t) (void *);
 #define PI_MEM_ALLOC_PAGEMAP 1
 #define PI_MEM_ALLOC_MAILBOX 2
 
+/* gpioCfgInternals */
+
+#define PI_CFG_DBG_LEVEL         0 /* bits 0-3 */
+#define PI_CFG_ALERT_FREQ        4 /* bits 4-7 */
+#define PI_CFG_RT_PRIORITY       (1<<8)
+#define PI_CFG_STATS             (1<<9)
+
+#define PI_CFG_ILLEGAL_VAL       (1<<10)
+
+/* gpioISR */
+
+#define RISING_EDGE  0
+#define FALLING_EDGE 1
+#define EITHER_EDGE  2
+
+
 /*F*/
 int gpioInitialise(void);
 /*D
@@ -1212,7 +1244,7 @@ void aFunction(int gpio, int level, uint32_t tick)
 
 // call aFunction whenever gpio 4 changes state
 
-gpioSetAlertFunc(4, aFunction);
+gpioSetAlertFunc(4F, aFunction);
 ...
 D*/
 
@@ -1244,6 +1276,89 @@ See [*gpioSetAlertFunc*] for further details.
 D*/
 
 
+/*F*/
+int gpioSetISRFunc(
+   unsigned user_gpio, unsigned edge, int timeout, gpioISRFunc_t f);
+/*D
+Registers a function to be called (a callback) whenever the specified
+gpio interrupt occurs.
+
+. .
+user_gpio: 0-31
+     edge: RISING_EDGE, FALLING_EDGE, or EITHER_EDGE
+  timeout: interrupt timeout in milliseconds (<=0 to cancel)
+        f: the callback function
+. .
+
+Returns 0 if OK, otherwise PI_BAD_USER_GPIO, PI_BAD_EDGE,
+or PI_BAD_ISR_INIT.
+
+One function may be registered per gpio.
+
+The function is passed the gpio, the current level, and the
+current tick.  The level will be PI_TIMEOUT if the optional
+interrupt timeout expires.
+
+The underlying Linux sysfs gpio interface is used to provide
+the interrupt services.
+
+The first time the function is called, with a non-NULL f, the
+gpio is exported, set to be an input, and set to interrupt
+on the given edge and timeout.
+
+Subsequent calls, with a non-NULL f, can vary one or more of the
+edge, timeout, or function.
+
+The ISR may be cancelled by passing a NULL f, in which case the
+gpio is unexported.
+
+The tick is that read at the time the process was informed of
+the interrupt.  This will be a variable number of microseconds
+after the interrupt occurred.  Typically the latency will be of
+the order of 50 microseconds.  The latency is not guaranteed
+and will vary with system load.
+
+The level is that read at the time the process was informed of
+the interrupt, or PI_TIMEOUT if the optional interrupt timeout
+expired.  It may not be the same as the expected edge as
+interrupts happening in rapid succession may be missed by the
+kernel (i.e. this mechanism can not be used to capture several
+interrupts only a few microseconds apart).
+D*/
+
+
+/*F*/
+int gpioSetISRFuncEx(
+   unsigned user_gpio,
+   unsigned edge,
+   int timeout,
+   gpioISRFuncEx_t f,
+   void *userdata);
+/*D
+Registers a function to be called (a callback) whenever the specified
+gpio interrupt occurs.
+
+. .
+user_gpio: 0-31
+     edge: RISING_EDGE, FALLING_EDGE, or EITHER_EDGE
+  timeout: interrupt timeout in milliseconds (<=0 to cancel)
+        f: the callback function
+ userdata: pointer to arbitrary user data
+. .
+
+Returns 0 if OK, otherwise PI_BAD_USER_GPIO, PI_BAD_EDGE,
+or PI_BAD_ISR_INIT.
+
+The function is passed the gpio, the current level, the
+current tick, and the userdata pointer.
+
+Only one of [*gpioSetISRFunc*] or [*gpioSetISRFuncEx*] can be
+registered per gpio.
+
+See [*gpioSetISRFunc*] for further details.
+D*/
+
+
 /*F*/
 int gpioNotifyOpen(void);
 /*D
@@ -1654,10 +1769,13 @@ Delays between waves may be added with the delay command.
 
 The following command codes are supported:
 
-Name        @ Cmd & Data @ Meaning
-Loop Start  @ 255 0      @ Identify start of a wave block
-Loop Repeat @ 255 1 x y  @ loop x + y*256 times
-Delay       @ 255 2 x y  @ delay x + y*256 microseconds
+Name         @ Cmd & Data @ Meaning
+Loop Start   @ 255 0      @ Identify start of a wave block
+Loop Repeat  @ 255 1 x y  @ loop x + y*256 times
+Delay        @ 255 2 x y  @ delay x + y*256 microseconds
+Loop Forever @ 255 3      @ loop forever
+
+If present Loop Forever must be the last entry in the chain.
 
 The code is currently dimensioned to support a chain with roughly
 600 entries and 20 loop counters.
@@ -3540,7 +3658,7 @@ size is requested with [*gpioCfgBufferSize*].
 D*/
 
 /*F*/
-int gpioCfgInternals(unsigned cfgWhat, int cfgVal);
+int gpioCfgInternals(unsigned cfgWhat, unsigned cfgVal);
 /*D
 Used to tune internal settings.
 
@@ -3550,6 +3668,25 @@ cfgWhat: see source code
 . .
 D*/
 
+/*F*/
+uint32_t gpioCfgGetInternals(void);
+/*D
+This function returns the current library internal configuration
+settings.
+D*/
+
+/*F*/
+int gpioCfgSetInternals(uint32_t cfgVal);
+/*D
+This function sets the current library internal configuration
+settings.
+
+. .
+cfgVal: see source code
+. .
+D*/
+
+
 /*F*/
 int gpioCustom1(unsigned arg1, unsigned arg2, char *argx, unsigned argc);
 /*D
@@ -3904,8 +4041,8 @@ clkfreq::4689-250M
 The hardware clock frequency.
 
 . .
-#define PI_HW_CLK_MIN_FREQ 4689
-#define PI_HW_CLK_MAX_FREQ 250000000
+PI_HW_CLK_MIN_FREQ 4689
+PI_HW_CLK_MAX_FREQ 250000000
 . .
 
 count::
@@ -3919,8 +4056,8 @@ The number of data bits to be used when adding serial data to a
 waveform.
 
 . .
-#define PI_MIN_WAVE_DATABITS 1
-#define PI_MAX_WAVE_DATABITS 32
+PI_MIN_WAVE_DATABITS 1
+PI_MAX_WAVE_DATABITS 32
 . .
 
 DMAchannel::0-14
@@ -3940,6 +4077,16 @@ A number representing the ratio of on time to off time for PWM.
 The number may vary between 0 and range (default 255) where
 0 is off and range is fully on.
 
+edge::0-2
+The type of gpio edge to generate an intrrupt.  See[*gpioSetISRFunc*],
+and [*gpioSetISRFuncEx*].
+
+. .
+RISING_EDGE 0
+FALLING_EDGE 1
+EITHER_EDGE 2
+. .
+
 f::
 
 A function.
@@ -4018,6 +4165,18 @@ typedef void (*gpioGetSamplesFuncEx_t)
    (const gpioSample_t *samples, int numSamples, void *userdata);
 . .
 
+gpioISRFunc_t::
+. .
+typedef void (*gpioISRFunc_t)
+   (int gpio, int level, uint32_t tick);
+. .
+
+gpioISRFuncEx_t::
+. .
+typedef void (*gpioISRFuncEx_t)
+   (int gpio, int level, uint32_t tick, void *userdata);
+. .
+
 gpioPulse_t::
 . .
 typedef struct
@@ -4264,15 +4423,15 @@ PWMduty::0-1000000 (1M)
 The hardware PWM dutycycle.
 
 . .
-#define PI_HW_PWM_RANGE 1000000
+PI_HW_PWM_RANGE 1000000
 . .
 
 PWMfreq::5-250K
 The hardware PWM frequency.
 
 . .
-#define PI_HW_PWM_MIN_FREQ 1
-#define PI_HW_PWM_MAX_FREQ 125000000
+PI_HW_PWM_MIN_FREQ 1
+PI_HW_PWM_MAX_FREQ 125000000
 . .
 
 range::25-40000
@@ -4426,20 +4585,28 @@ The number of (half) stop bits to be used when adding serial data
 to a waveform.
 
 . .
-#define PI_MIN_WAVE_HALFSTOPBITS 2
-#define PI_MAX_WAVE_HALFSTOPBITS 8
+PI_MIN_WAVE_HALFSTOPBITS 2
+PI_MAX_WAVE_HALFSTOPBITS 8
 . .
 
 *str::
 An array of characters.
 
 timeout::
-A gpio watchdog timeout in milliseconds.
+A gpio level change timeout in milliseconds.
+
+[*gpioSetWatchdog*]
 . .
 PI_MIN_WDOG_TIMEOUT 0
 PI_MAX_WDOG_TIMEOUT 60000
 . .
 
+[*gpioSetISRFunc*] and [*gpioSetISRFuncEx*]
+. .
+<=0 cancel timeout
+>0 timeout after specified milliseconds
+. .
+
 timer::
 . .
 PI_MIN_TIMER 0
@@ -4616,6 +4783,9 @@ PARAMS*/
 
 #define PI_CMD_SLRI  94
 
+#define PI_CMD_CGI   95
+#define PI_CMD_CSI   96
+
 #define PI_CMD_NOIB  99
 
 /*DEF_E*/
@@ -4802,6 +4972,9 @@ after this command is issued.
 #define PI_CHAIN_TOO_BIG   -119 // chain is too long
 #define PI_DEPRECATED      -120 // deprecated function removed
 #define PI_BAD_SER_INVERT  -121 // bit bang serial invert not 0 or 1
+#define PI_BAD_EDGE        -122 // bad ISR edge value, not 0-2
+#define PI_BAD_ISR_INIT    -123 // bad ISR initialisation
+#define PI_BAD_FOREVER     -124 // loop forever must be last chain command
 
 #define PI_PIGIF_ERR_0    -2000
 #define PI_PIGIF_ERR_99   -2099
@@ -4830,6 +5003,8 @@ after this command is issued.
 #define PI_DEFAULT_UPDATE_MASK_COMPUTE   0x00FFFFFFFFFFFFLL
 #define PI_DEFAULT_MEM_ALLOC_MODE        PI_MEM_ALLOC_AUTO
 
+#define PI_DEFAULT_CFG_INTERNALS         0
+
 /*DEF_E*/
 
 #endif
index a1b3544a810a866490ad021c69c6c403f04367a1..2771dfbe1ddc5f2f30f648be31c30081dd79b13b 100644 (file)
--- a/pigpio.py
+++ b/pigpio.py
@@ -264,7 +264,7 @@ import threading
 import os
 import atexit
 
-VERSION = "1.21"
+VERSION = "1.22"
 
 exceptions = True
 
@@ -433,7 +433,7 @@ _PI_CMD_I2CZ =92
 
 _PI_CMD_WVCHA=93
 
-_PI_CMD_SLRI94
+_PI_CMD_SLRI =94
 
 # pigpio error numbers
 
@@ -899,7 +899,7 @@ class _callback_thread(threading.Thread):
    def run(self):
       """Runs the notification thread."""
 
-      lastLevel = 0
+      lastLevel = _pigpio_command(self.control,  _PI_CMD_BR1, 0, 0)
 
       MSG_SIZ = 12
 
@@ -2014,10 +2014,13 @@ class pi():
 
       The following command codes are supported:
 
-      Name        @ Cmd & Data @ Meaning
-      Loop Start  @ 255 0      @ Identify start of a wave block
-      Loop Repeat @ 255 1 x y  @ loop x + y*256 times
-      Delay       @ 255 2 x y  @ delay x + y*256 microseconds
+      Name         @ Cmd & Data @ Meaning
+      Loop Start   @ 255 0      @ Identify start of a wave block
+      Loop Repeat  @ 255 1 x y  @ loop x + y*256 times
+      Delay        @ 255 2 x y  @ delay x + y*256 microseconds
+      Loop Forever @ 255 3      @ loop forever
+
+      If present Loop Forever must be the last entry in the chain.
 
       The code is currently dimensioned to support a chain with
       roughly 600 entries and 20 loop counters.
index 3152b1612b0e04969af7c042600c003b29e3f40f..a54d7b6b8f5aef6ad33126df19b8d913e316ac86 100644 (file)
--- a/pigpiod.1
+++ b/pigpiod.1
@@ -39,6 +39,11 @@ gpio sample buffer in milliseconds
 100-10000
 default 120
 
+.IP "\fB-c value\fP"
+library internal settings
+
+default 0
+
 .IP "\fB-d value\fP"
 primary DMA channel
 0-14
@@ -158,13 +163,18 @@ affect updateable gpios.
 .br
 
 .br
-There are two special cases.
+There are several special cases.
+
+.br
+
+.br
+The activity LED (green) may be written (gpio 16 for type 1 and 2
+boards, gpio 47 for type 3 boards)
 
 .br
 
 .br
-The activity LED may be written (gpio 16 for type 1 and 2
-boards, gpio 47 for type 3 boards).
+The power LED (red) may be written on type 3 boards (gpio 35).
 
 .br
 
index da4fb8af8d9e5906df1506643c8a868f592678e4..015b0d5498189e9775269bad8c36651ffa0dda32 100644 (file)
--- a/pigpiod.c
+++ b/pigpiod.c
@@ -26,7 +26,7 @@ For more information, please refer to <http://unlicense.org/>
 */
 
 /*
-This version is for pigpio version 30+
+This version is for pigpio version 38+
 */
 
 #include <sys/types.h>
@@ -59,6 +59,8 @@ static unsigned socketPort             = PI_DEFAULT_SOCKET_PORT;
 static unsigned memAllocMode           = PI_DEFAULT_MEM_ALLOC_MODE;
 static uint64_t updateMask             = -1;
 
+static uint32_t cfgInternals           = PI_DEFAULT_CFG_INTERNALS;
+
 static int updateMaskSet = 0;
 
 static FILE * errFifo;
@@ -85,6 +87,7 @@ void usage()
       "Usage: sudo pigpiod [OPTION] ...\n" \
       "   -a value, DMA mode, 0=AUTO, 1=PMAP, 2=MBOX,   default AUTO\n" \
       "   -b value, gpio sample buffer in milliseconds, default 120\n" \
+      "   -c value, library internal settings,          default 0\n" \
       "   -d value, primary DMA channel, 0-14,          default 14\n" \
       "   -e value, secondary DMA channel, 0-6,         default 5\n" \
       "   -f,       disable fifo interface,             default enabled\n" \
@@ -100,41 +103,56 @@ void usage()
    "\n");
 }
 
+static uint64_t getNum(char *str, int *err)
+{
+   uint64_t val;
+   char *endptr;
+
+   *err = 0;
+   val = strtoll(str, &endptr, 0);
+   if (*endptr) {*err = 1; val = -1;}
+   return val;
+}
+
 static void initOpts(int argc, char *argv[])
 {
-   int i, opt;
-   uint64_t mask;
-   char * endptr;
+   int opt, err, i;
+   int64_t mask;
 
-   while ((opt = getopt(argc, argv, "a:b:d:e:fkp:s:t:x:")) != -1)
+   while ((opt = getopt(argc, argv, "a:b:c:d:e:fkp:s:t:x:")) != -1)
    {
-      i = -1;
-
       switch (opt)
       {
          case 'a':
-            i = atoi(optarg);
+            i = getNum(optarg, &err);
             if ((i >= PI_MEM_ALLOC_AUTO) && (i <= PI_MEM_ALLOC_MAILBOX))
                memAllocMode = i;
             else fatal("invalid -a option (%d)", i);
             break;
 
          case 'b':
-            i = atoi(optarg);
+            i = getNum(optarg, &err);
             if ((i >= PI_BUF_MILLIS_MIN) && (i <= PI_BUF_MILLIS_MAX))
                bufferSizeMilliseconds = i;
             else fatal("invalid -b option (%d)", i);
             break;
 
+         case 'c':
+            i = getNum(optarg, &err);
+            if ((i >= 0) && (i < PI_CFG_ILLEGAL_VAL))
+               cfgInternals = i;
+            else fatal("invalid -c option (%x)", i);
+            break;
+
          case 'd':
-            i = atoi(optarg);
+            i = getNum(optarg, &err);
             if ((i >= PI_MIN_DMA_CHANNEL) && (i <= PI_MAX_PRIMARY_CHANNEL))
                DMAprimaryChannel = i;
             else fatal("invalid -d option (%d)", i);
             break;
 
          case 'e':
-            i = atoi(optarg);
+            i = getNum(optarg, &err);
             if ((i >= PI_MIN_DMA_CHANNEL) && (i <= PI_MAX_SECONDARY_CHANNEL))
                DMAsecondaryChannel = i;
             else fatal("invalid -e option (%d)", i);
@@ -149,14 +167,14 @@ static void initOpts(int argc, char *argv[])
             break; 
 
          case 'p':
-            i = atoi(optarg);
+            i = getNum(optarg, &err);
             if ((i >= PI_MIN_SOCKET_PORT) && (i <= PI_MAX_SOCKET_PORT))
                socketPort = i;
             else fatal("invalid -p option (%d)", i);
             break;
 
          case 's':
-            i = atoi(optarg);
+            i = getNum(optarg, &err);
 
             switch(i)
             {
@@ -176,15 +194,15 @@ static void initOpts(int argc, char *argv[])
             break;
 
          case 't':
-            i = atoi(optarg);
+            i = getNum(optarg, &err);
             if ((i >= PI_CLOCK_PWM) && (i <= PI_CLOCK_PCM))
                clockPeripheral = i;
             else fatal("invalid -t option (%d)", i);
             break;
 
          case 'x':
-            mask = strtoll(optarg, &endptr, 0);
-            if (!*endptr)
+            mask = getNum(optarg, &err);
+            if (!err)
             {
                updateMask = mask;
                updateMaskSet = 1;
@@ -273,6 +291,8 @@ int main(int argc, char **argv)
 
    if (updateMaskSet) gpioCfgPermissions(updateMask);
 
+   gpioCfgSetInternals(cfgInternals);
+
    /* start library */
 
    if (gpioInitialise()< 0) fatal("Can't initialise pigpio library");
index 77eddcfd6addb9b3ea4480938a6c774bb296a1e1..3e29b501139536a8a1d4d401d1b77887f7ac815b 100644 (file)
@@ -1735,18 +1735,26 @@ The following command codes are supported:
 .br
 
 .br
-Name          Cmd & Data   Meaning
+Name           Cmd & Data   Meaning
 
 .br
-Loop Start    255 0        Identify start of a wave block
+Loop Start     255 0        Identify start of a wave block
 
 .br
-Loop Repeat   255 1 x y    loop x + y*256 times
+Loop Repeat    255 1 x y    loop x + y*256 times
 
 .br
-Delay         255 2 x y    delay x + y*256 microseconds
+Delay          255 2 x y    delay x + y*256 microseconds
 
 .br
+Loop Forever   255 3        loop forever
+
+.br
+
+.br
+
+.br
+If present Loop Forever must be the last entry in the chain.
 
 .br
 
index 93b83e4baa45434859c8d9560fcb41c79e3f48df..e36a0fd6f9c35e0cbd1ff85d7e505c50c8dbfbf2 100644 (file)
@@ -25,7 +25,7 @@ OTHER DEALINGS IN THE SOFTWARE.
 For more information, please refer to <http://unlicense.org/>
 */
 
-/* PIGPIOD_IF_VERSION 18 */
+/* PIGPIOD_IF_VERSION 19 */
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -80,6 +80,7 @@ static int gPigHandle = -1;
 static int gPigNotify = -1;
 
 static uint32_t gNotifyBits;
+static uint32_t gLastLevel;
 
 callback_t *gCallBackFirst = 0;
 callback_t *gCallBackLast = 0;
@@ -220,8 +221,6 @@ static int pigpioOpenSocket(char *addr, char *port)
 
 static void dispatch_notification(gpioReport_t *r)
 {
-   static uint32_t lastLevel = 0;
-
    callback_t *p;
    uint32_t changed;
    int l, g;
@@ -233,9 +232,9 @@ static void dispatch_notification(gpioReport_t *r)
 
    if (r->flags == 0)
    {
-      changed = (r->level ^ lastLevel) & gNotifyBits;
+      changed = (r->level ^ gLastLevel) & gNotifyBits;
 
-      lastLevel = r->level;
+      gLastLevel = r->level;
 
       p = gCallBackFirst;
 
@@ -487,6 +486,7 @@ void stop_thread(pthread_t *pth)
    {
       pthread_cancel(*pth);
       pthread_join(*pth, NULL);
+      free(pth);
    }
 }
 
@@ -507,6 +507,8 @@ int pigpio_start(char *addrStr, char *portStr)
             if (gPigHandle < 0) return pigif_bad_noib;
             else
             {
+               gLastLevel = read_bank_1();
+
                pthNotify = start_thread(pthNotifyThread, 0);
                if (pthNotify)
                {
@@ -1540,3 +1542,4 @@ int wait_for_edge(unsigned user_gpio, unsigned edge, double timeout)
    return triggered;
 }
 
+
index ed68a99a4fd7db22372b906ca4ad5748147430d2..7d6a630cf5504e554b7f588211425f7dbcc4e930 100644 (file)
@@ -30,7 +30,7 @@ For more information, please refer to <http://unlicense.org/>
 
 #include "pigpio.h"
 
-#define PIGPIOD_IF_VERSION 18
+#define PIGPIOD_IF_VERSION 20
 
 /*TEXT
 
@@ -283,10 +283,6 @@ typedef void (*CBFuncEx_t)
 
 typedef struct callback_s callback_t;
 
-#define RISING_EDGE  0
-#define FALLING_EDGE 1
-#define EITHER_EDGE  2
-
 /*F*/
 double time_time(void);
 /*D
@@ -1207,10 +1203,13 @@ Delays between waves may be added with the delay command.
 
 The following command codes are supported:
 
-Name        @ Cmd & Data @ Meaning
-Loop Start  @ 255 0      @ Identify start of a wave block
-Loop Repeat @ 255 1 x y  @ loop x + y*256 times
-Delay       @ 255 2 x y  @ delay x + y*256 microseconds
+Name         @ Cmd & Data @ Meaning
+Loop Start   @ 255 0      @ Identify start of a wave block
+Loop Repeat  @ 255 1 x y  @ loop x + y*256 times
+Delay        @ 255 2 x y  @ delay x + y*256 microseconds
+Loop Forever @ 255 3      @ loop forever
+
+If present Loop Forever must be the last entry in the chain.
 
 The code is currently dimensioned to support a chain with roughly
 600 entries and 20 loop counters.
diff --git a/pigs.1 b/pigs.1
index 544d52ee89f0aa07d56b6ea960ae15076d406383..061255af15ade1c52f3303515917d0b41563a3af 100644 (file)
--- a/pigs.1
+++ b/pigs.1
@@ -500,6 +500,20 @@ customiser.
 
 .br
 
+.IP "\fBCGI \fP - Configuration get internals"
+.IP "" 4
+This command returns the value of the internal library
+configuration settings.
+
+.br
+
+.IP "\fBCSI v\fP - Configuration set internals"
+.IP "" 4
+This command sets the value of the internal library
+configuration settings to \fBv\fP.
+
+.br
+
 .IP "\fBGDC u\fP - Get gpio PWM dutycycle"
 .IP "" 4
 
@@ -2584,6 +2598,8 @@ will be returned.
 .br
 The invert parameter \fBv\fP is 1 for inverted signal, 0 for normal.
 
+.br
+
 \fBExample\fP
 .br
 
@@ -3170,13 +3186,17 @@ The following command codes are supported:
 .br
 
 .EX
-Name          Cmd & Data   Meaning
-Loop Start    255 0        Identify start of a wave block
-Loop Repeat   255 1 x y    loop x + y*256 times
-Delay         255 2 x y    delay x + y*256 microseconds
+Name           Cmd & Data   Meaning
+Loop Start     255 0        Identify start of a wave block
+Loop Repeat    255 1 x y    loop x + y*256 times
+Delay          255 2 x y    delay x + y*256 microseconds
+Loop Forever   255 3        loop forever
 
 .EE
 
+.br
+If present Loop Forever must be the last entry in the chain.
+
 .br
 The code is currently dimensioned to support a chain with roughly
 600 entries and 20 loop counters.
index 19a1a32726c2eb0938544f67ece3facc57899550..958444e560104b97336e477f5889ba582cc1801f 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -3,7 +3,7 @@
 from distutils.core import setup
 
 setup(name='pigpio',
-      version='1.21',
+      version='1.22',
       author='joan',
       author_email='joan@abyz.co.uk',
       maintainer='joan',
index 6435370f3992e5e1eef53e7071fde21fd6dfbcca..6cb8aefb01ca3daf93ac4e6c02a8e9c591ec1414 100755 (executable)
@@ -592,18 +592,27 @@ def t9():
 
    t9cb = pi.callback(GPIO)
 
+   old_exceptions = pigpio.exceptions
+
+   pigpio.exceptions = False
+
    s = pi.store_script(script)
 
    # Ensure the script has finished initing.
    while True:
-      time.sleep(0.1)
       e, p = pi.script_status(s)
       if e != pigpio.PI_SCRIPT_INITING:
          break
+      time.sleep(0.1)
 
    oc = t9cb.tally()
    pi.run_script(s, [99, GPIO])
-   time.sleep(2)
+   while True:
+      e, p = pi.script_status(s)
+      if e != pigpio.PI_SCRIPT_RUNNING:
+         break
+      time.sleep(0.1)
+   time.sleep(0.3)
    c = t9cb.tally() - oc
    CHECK(9, 1, c, 100, 0, "store/run script")
 
@@ -613,9 +622,9 @@ def t9():
       e, p = pi.script_status(s)
       if e != pigpio.PI_SCRIPT_RUNNING:
          break
-      time.sleep(0.5)
+      time.sleep(0.1)
+   time.sleep(0.3)
    c = t9cb.tally() - oc
-   time.sleep(0.1)
    CHECK(9, 2, c, 201, 0, "run script/script status")
 
    oc = t9cb.tally()
@@ -627,13 +636,15 @@ def t9():
       if p[9] < 1900:
          pi.stop_script(s)
       time.sleep(0.1)
+   time.sleep(0.3)
    c = t9cb.tally() - oc
-   time.sleep(0.1)
-   CHECK(9, 3, c, 110, 10, "run/stop script/script status")
+   CHECK(9, 3, c, 110, 20, "run/stop script/script status")
 
    e = pi.delete_script(s)
    CHECK(9, 4, e, 0, 0, "delete script")
 
+   pigpio.exceptions = old_exceptions
+
 def ta():
    print("Serial link tests.")
 
index 750b69743a6df2993f0bce46fc9d9c82c04f9926..2effc3271d71c719d9a6af0909a1bc7da2c90556 100644 (file)
@@ -362,7 +362,7 @@ To the lascivious pleasing of a lute.\n\
    oc = t5_count;
    time_sleep(5.05);
    c = t5_count - oc;
-   CHECK(5, 4, c, 50, 1, "callback");
+   CHECK(5, 4, c, 50, 2, "callback");
 
    e = wave_tx_stop();
    CHECK(5, 5, e, 0, 0, "wave tx stop");
diff --git a/x_pigs b/x_pigs
index 923734a8b89b4473dd9b646790026d2acccd1dd3..a8b8d2179d9889019bffb1e845a37ad79dd000d2 100755 (executable)
--- a/x_pigs
+++ b/x_pigs
@@ -49,7 +49,7 @@ s=$(pigs bs2 0)
 if [[ $s = "" ]]; then echo "BS2 ok"; else echo "BS2 fail ($s)"; fi
 
 s=$(pigs h)
-if [[ ${#s} = 4321 ]]; then echo "HELP ok"; else echo "HELP fail (${#s})"; fi
+if [[ ${#s} = 4412 ]]; then echo "HELP ok"; else echo "HELP fail (${#s})"; fi
 
 s=$(pigs hwver)
 if [[ $s -ne 0 ]]; then echo "HWVER ok"; else echo "HWVER fail ($s)"; fi